<!doctype html>
<html lang="en">
	<head>
		<meta charset="utf-8">
		<title>FairEmail - Decrypt</title>
		<link rel="shortcut icon" href="https://raw.githubusercontent.com/M66B/FairEmail/master/app/src/main/ic_launcher-web.png">

		<meta name="theme-color" content="#006db3">
		<meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
		<meta name="description" content="Decrypt password protected content">
		<meta name="author" content="M66B">
		<meta name="robots" content="noindex">

		<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
		<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/Sources#sources -->
		<meta
			http-equiv="Content-Security-Policy"
			content="default-src 'self' 'unsafe-inline'; img-src data: https://raw.githubusercontent.com/M66B/FairEmail/master/app/src/main/ic_launcher-web.png;">

		<style>
			body { padding-left: 10px; padding-right: 10px; font-family: Arial, Helvetica, sans-serif; }
			.button { display: inline-block; text-align: center; cursor: pointer; }
			.button span:first-child { font-size: x-large; }
			.button span:last-child { font-size: smaller; }
		</style>

		<style>
			.noscript { display: none; }
		</style>
		<noscript>
			<style>
				.noscript { display: block; }
			</style>
		</noscript>

		<!-- https://github.com/cure53/DOMPurify 2.4.0 -->
		<script src="purify.min.js"></script>

		<script>
			window.addEventListener('load', load);

			function load() {
				const form = document.getElementById('form')
				const message = document.getElementById('message');
				const copy = document.getElementById('copy');
				const email = document.getElementById('email');
				const close = document.getElementById('close');
				const error = document.getElementById('error');
				const details = document.getElementById('details');
				const year = document.getElementById('year');

				form.addEventListener('submit', submit);

				if (window.location.hash)
					if (crypto.subtle &&
						typeof Uint8Array === 'function' &&
						typeof TextEncoder === 'function') {
						form.style.display = 'block';
						form.password.focus();
					}
					else {
						error.textContent = 'Your browser is unsuitable for decrypting content';
						error.style.display = 'block';
						details.innerHTML =
							'crypto.subtle: ' + (crypto.subtle ? 'Yes' : 'No') + '<br>' +
							'Uint8Array: ' + (Uint8Array ? 'Yes' : 'No') + '<br>' +
							'TextEncoder: ' + (TextEncoder ? 'Yes' : 'No') + '<br>';
						details.style.display = 'block';
					}
				else {
					error.textContent = 'Nothing to see here';
					error.style.display = 'block';
				}

				copy.onclick = function (event) {
					event.preventDefault();

					const blob = new Blob([message.innerHTML], { type: 'text/html' });
					const clip = new ClipboardItem({ 'text/html': blob });

					navigator.clipboard.write([clip]).then(function() {
						alert('Copied to clipboard');
					}, function() {
						alert('Copy failed');
					});
				}

				email.onclick = function (event) {
					event.preventDefault();
					window.location.href = "mailto:?body=" + encodeURIComponent(message.textContent);
				}

				close.onclick = function (event) {
					event.preventDefault();
					form.fields.disabled = false;
					form.style.display = 'block';
					content.style.display = 'none';
					message.innerHTML = '';
					form.password.focus();
				}

				year.textContent = new Date().getFullYear();
			}

			function submit(event) {
				event.preventDefault();
				decrypt();
			}

			async function decrypt() {
				const form = document.getElementById('form')
				const content = document.getElementById('content');
				const message = document.getElementById('message');
				const error = document.getElementById('error');
				const details = document.getElementById('details');

				try {
					form.fields.disabled = true;
					content.style.display = 'none';
					error.style.display = 'none';
					details.style.display = 'none';

					if (!form.password.value)
						throw new Error('Password required');

					const dirty = await _decrypt(form.password.value);
					const clean = DOMPurify.sanitize(dirty, { USE_PROFILES: { html: true }, SANITIZE_NAMED_PROPS: true });

					form.password.value = '';
					message.innerHTML = clean;

					var a = message.getElementsByTagName('a');
					for (let i = 0; i < a.length; i++)
						if (a[i].href) {
							a[i].rel = 'noopener noreferrer';
							a[i].setAttribute('target', '_blank');
							a[i].onclick = function() {
								return confirm('Go to ' + a[i].href + ' ?');
							};
						}

					form.style.display = 'none';
					content.style.display = 'block';
				} catch (e) {
					console.log("%O", e);
					form.fields.disabled = false;
					form.password.value = '';
					form.password.focus();
					error.textContent = 'Could not decrypt the message. Is the password correct?';
					error.style.display = 'block';
					details.textContent = e.toString();
					details.style.display = 'block';
				}
			}

			async function _decrypt(password) {
				const msg = atob(window.location.hash.substr(1).replaceAll('-', '+').replaceAll('_', '/'));

				const buf = new Uint8Array(msg.length);
				for (let i = 0; i < msg.length; i++)
					buf[i] = msg.charCodeAt(i);

				const version = buf[0];
				const salt = buf.slice(1, 1 + 16);
				const iv = buf.slice(1 + 16, 1 + 16 + 12);
				const e = buf.slice(1 + 16 + 12, buf.length);

				// https://developer.mozilla.org/en-US/docs/Web/API/Crypto/subtle
				// https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder
				const passwordBuffer = new TextEncoder('UTF-8').encode(password);
				const importedKey = await crypto.subtle.importKey('raw', passwordBuffer, 'PBKDF2', false, ['deriveBits']);
				const derivation = await crypto.subtle.deriveBits({name: 'PBKDF2', hash: 'SHA-512', salt: salt, iterations: 120000}, importedKey, 256);
				const importedEncryptionKey = await crypto.subtle.importKey('raw', derivation, {name: 'AES-GCM'}, false, ['decrypt']);
				const decrypted = await crypto.subtle.decrypt({name: 'AES-GCM', iv: iv, tagLength: 128}, importedEncryptionKey, e);

				return new TextDecoder('UTF-8').decode(decrypted);
			}
		</script>
	</head>
	<body>
		<p class="noscript" style="color: red; font-weight: bold;">Please enable JavaScript</p>

		<form id="form" action="#" method="GET" style="display: none;">
			<p>
				Someone sent you password protected content with <a href="https://email.faircode.eu/" target="_blank">FairEmail</a>.
			</p>
			<p>
				The sender should have provided the password.
			</p>
			<hr>
			<fieldset id="fields" style="border:0 none; margin: 0; padding: 0;">
				<p>
					<label for="password">Enter password &#x1F511;</label><br>
					<input id="password" name="password" type="password" required><br>
					<span style="font-size: smaller;">Passwords are case-sensitive </span>
				</p>
				<p>
					<input id="submit" style="padding: 3px;" type="submit" value="&#x1F513; Decrypt">
				</p>
			</fieldset>
			<p style="font-size: smaller;">
				Passwords, encrypted, and decrypted content stay in your own browser. See <a href="https://github.com/M66B/FairEmail/blob/master/FAQ.md#user-content-faq184" target="_blank">here</a> for more information.
			</p>
			<hr>
		</form>

		<div id="content" style="display: none; width: 100%;">
			<p>The sender sent you this password protected content with <a href="https://email.faircode.eu/" target="_blank">FairEmail</a>:</p>

			<hr style="margin-top: 30px;">
			<p id="message" style="width: 100%; font-size: larger;"></p>
			<hr style="margin-bottom: 30px;">

			<div>
				<div id="copy" class="button">
					<span>&#x1f4cb;</span><br>
					<span>Copy</span>
				</div>
				&ensp;
				<div id="email" class="button">
					<span>&#x1F4E7;</span><br>
					<span>Email</span>
				</div>
				&ensp;
				<div id="close" class="button">
					<span>&#x2715;</span><br>
					<span>Close</span>
				</div>
			</div>
		</div>

		<p id="error" style="color: red; font-weight: bold; display: none;"></p>

		<p id="details" style="font-size: x-small; display: none;"></p>

		<p style="padding-top: 30px;">
			Copyright &copy; 2018&ndash;<span id="year">2022</span> by Marcel Bokhorst (M66B)
			<br>
			<br>
			<span style="font-size: smaller;">This page is <a href="https://github.com/M66B/FairEmail/blob/master/decrypt/index.html" target="_blank">open source</a>.</span>
		</p>
	</body>
</html>
